﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;

namespace MoneyBook.Reflection {
    public class EnhancePropertyAttributePropertyInfo : PropertyInfo {

        private PropertyInfo sourceProperty;
        private Type sourceType;
        private object[] customAttributes = null;

        public EnhancePropertyAttributePropertyInfo(PropertyInfo sourceProperty, Type sourceType) {
            this.sourceProperty = sourceProperty;
            this.sourceType = sourceType;
        }

        public override PropertyAttributes Attributes {
            get {
                return this.sourceProperty.Attributes;
            }
        }

        public override bool CanRead {
            get { return this.sourceProperty.CanRead; }
        }

        public override bool CanWrite {
            get { return this.sourceProperty.CanWrite; }
        }

        public override MethodInfo[] GetAccessors(bool nonPublic) {
            return this.sourceProperty.GetAccessors(nonPublic);
        }

        public override MethodInfo GetGetMethod(bool nonPublic) {
            return this.sourceProperty.GetGetMethod(nonPublic);
        }

        public override ParameterInfo[] GetIndexParameters() {
            return this.sourceProperty.GetIndexParameters();
        }

        public override MethodInfo GetSetMethod(bool nonPublic) {
            return this.sourceProperty.GetSetMethod(nonPublic);
        }

        public override object GetValue(object obj, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) {
            return this.sourceProperty.GetValue(obj, invokeAttr, binder, index, culture);
        }

        public override Type PropertyType {
            get { return this.sourceProperty.PropertyType; }
        }

        public override void SetValue(object obj, object value, BindingFlags invokeAttr, Binder binder, object[] index, System.Globalization.CultureInfo culture) {
            this.sourceProperty.SetValue(obj, value, invokeAttr, binder, index, culture);
        }

        public override Type DeclaringType {
            get { return this.sourceProperty.DeclaringType; }
        }

        //TODO
        public override object[] GetCustomAttributes(Type attributeType, bool inherit) {

            return this.GetCustomAttributes(inherit).Where(o => o.GetType() == attributeType).ToArray();
        }

        //TODO
        public override object[] GetCustomAttributes(bool inherit) {
            if (customAttributes != null) {
                return customAttributes;
            }
            object[] attributes = this.sourceProperty.GetCustomAttributes(inherit);

            var sourceTypeAttr = this.sourceType.GetSingleCustomAttribute
                <EnhancePropertyAttributeSourcesAttribute>(true);

            if (sourceTypeAttr == null) {
                customAttributes = attributes;
                return customAttributes;
            }
            if (sourceTypeAttr.Sources.Length < 1) {
                customAttributes = attributes;
                return customAttributes;
            }

            List<object> addedAttrs = new List<object>(attributes);

            var excludeAttr = this.sourceProperty.GetSingleCustomAttribute
                <ExcludeEnhancePropertyAttributeTypesAttribute>(true);

            List<string> targetPropNames = new List<string>();
            var targetPropNameAttr = this.sourceProperty.GetSingleCustomAttribute
                <MapEnhancePropertyAttributePropertiesAttribute>(true);
            if (targetPropNameAttr != null && targetPropNameAttr.PropertyNames != null && targetPropNameAttr.PropertyNames.Length > 0) {
                targetPropNames.AddRange(targetPropNameAttr.PropertyNames);
            } else {
                targetPropNames.Add(this.sourceProperty.Name);
            }

            foreach (Type targetType in sourceTypeAttr.Sources) {

                foreach (string targetPropName in targetPropNames) {

                    PropertyInfo targetTypeProperty = targetType.GetProperty(targetPropName);

                    if (targetTypeProperty == null) {
                        continue;
                    }

                    foreach (Attribute targetTypePropAttr in targetTypeProperty.GetCustomAttributes(inherit)) {
                        if (excludeAttr != null && excludeAttr.AttributeTypes.Any(t => t == targetTypePropAttr.GetType())) {
                            continue;
                        }
                        if (addedAttrs.Any(a => a.GetType() == targetTypePropAttr.GetType())) {
                            if (!targetTypePropAttr.AllowMultiple()) {
                                continue;
                            }
                        }
                        addedAttrs.Add(targetTypePropAttr);
                    }
                }
            }
            customAttributes = addedAttrs.ToArray();
            return customAttributes;
        }

        public override bool IsDefined(Type attributeType, bool inherit) {
            return this.sourceProperty.IsDefined(attributeType, inherit);
        }

        public override string Name {
            get { return this.sourceProperty.Name; }
        }

        public override Type ReflectedType {
            get { return this.sourceProperty.ReflectedType; }
        }

    }
}
